//Copyright (c) Microsoft Corporation.  All rights reserved.

using System;
using System.Text;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Eas.WebCrawler.Interfaces;

namespace Eas.WebCrawler.Test.Scenario {
    /// <summary>
    /// Summary description for ErrorScenarios
    /// </summary>
    [TestClass]
    public class ErrorScenarios {
        public ErrorScenarios() {
            //
            // TODO: Add constructor logic here
            //
        }
        #region Additional test attributes
        //
        // You can use the following additional attributes as you write your tests:
        //
        // Use ClassInitialize to run code before running the first test in the class
        // [ClassInitialize()]
        // public static void MyClassInitialize(TestContext testContext) { }
        //
        // Use ClassCleanup to run code after all tests in a class have run
        // [ClassCleanup()]
        // public static void MyClassCleanup() { }
        //

        NMock2.Mockery _Mockery = null;
        Eas.WebCrawler.Common.Test.TestDirectory _Directory = null;
        TestEventHandler _Handler = null;
        Dictionary<Uri, object> _ExpectedUris = null;

        // Use TestInitialize to run code before running each test 
        [TestInitialize()]
        public void MyTestInitialize() {
            _ExpectedUris = new Dictionary<Uri, object>();
            TestGateway.Clear();
            LogErrorAction.errors.Clear();
            LogTraceAction.traces.Clear();
            LogWebFailureAction.failures.Clear();
            _Mockery = new NMock2.Mockery();
            _Directory = new Eas.WebCrawler.Common.Test.TestDirectory(this.GetType().Name, "Resources");
            _Handler = _Mockery.NewMock<TestEventHandler>();
        }

        //
        // Use TestCleanup to run code after each test has run
        [TestCleanup()]
        public void MyTestCleanup() {
            Assert.AreEqual<int>(0, _ExpectedUris.Count);
            _Directory.Dispose();
            _Directory = null;
            _Handler = null;
            _Mockery.VerifyAllExpectationsHaveBeenMet();
            _Mockery.Dispose();
            _Mockery = null;
        }

        //
        #endregion




        [TestMethod]
        /// <summary>Executes the FailBeginAction test.</summary>
        public void FailBeginAction() {
            TestGateway._ThrowFor = ThrowFor.BeginGetResponse;
            string[] startingUris = new string[] { "test.html" };
            string[] processedUris = new string[] { };
            string[] historyFiles = new string[] { };
            int workerCount = 1;
            int errorCount = 0;
            int webFailureCount = 1;
            PerformTest(startingUris, processedUris, historyFiles, workerCount, errorCount, webFailureCount, null);
        }

        [TestMethod]
        /// <summary>Executes the AnotherFailOnBegin test.</summary>
        public void AnotherFailOnBegin() {
            string[] startingUris = new string[] { "test.html" };
            string[] processedUris = new string[] { "test.html" };
            string[] historyFiles = new string[] { };
            int workerCount = 1;
            int errorCount = 0;
            int webFailureCount = 1;

            LoadHistoryFiles(historyFiles, _Directory);
            string[] initialUriList = CreateUriList(startingUris, _Directory);
            using (Eas.WebCrawler.Interfaces.WebCrawlerEngine engine = TestHelper.CreateEngine(new TestGateway(), _Handler, _Directory, workerCount, -1, initialUriList)) {
                NMock2.IAction failOnBeginAction = new SetFailOnBeginAction(engine);

                Eas.WebCrawler.Common.Threading.EventHandlerCompletionEvent waitEvent = PrepareEngine(_Handler, engine);
                ExpectProcessedUris(processedUris, failOnBeginAction, _Handler, _Directory, engine);
                RunEngine(engine, waitEvent);
                CheckErrors(errorCount, webFailureCount);
            }
        }

        [TestMethod]
        /// <summary>Executes the FailFromReportTransitionTrace test.</summary>
        public void FailFromReportTransitionTrace() {
            string[] startingUris = new string[] { "test.html" };
            string[] processedUris = new string[] { "test.html", "nolinks.html" };
            string[] historyFiles = new string[] { };
            int workerCount = 1;
            int errorCount = 1;
            int webFailureCount = 0;

            LoadHistoryFiles(historyFiles, _Directory);
            string[] initialUriList = CreateUriList(startingUris, _Directory);
            using (Eas.WebCrawler.Interfaces.WebCrawlerEngine engine = TestHelper.CreateEngine(new TestGateway(), _Handler, _Directory, workerCount, -1, initialUriList)) {
                Eas.WebCrawler.Common.Threading.EventHandlerCompletionEvent waitEvent = new Eas.WebCrawler.Common.Threading.EventHandlerCompletionEvent();
                engine.Events.Stopped += waitEvent.CreateEventHandler();
                engine.Events.TraceLevel = TraceLevel.Detailed;
                Assert.AreEqual<TraceLevel>(TraceLevel.Detailed, engine.Events.TraceLevel);
                LogErrorAction.errors.Clear();
                NMock2.Expect.AtLeast(0).On(_Handler).Method("Error").WithAnyArguments().Will(new LogErrorAction());
                NMock2.Expect.AtLeast(0).On(_Handler).Method("Trace").WithAnyArguments().Will(new LogTraceAction(true));
                NMock2.Expect.Once.On(_Handler).Method("BeforeStart").WithAnyArguments();
                NMock2.Expect.Once.On(_Handler).Method("AfterStop").WithAnyArguments();
                NMock2.Expect.AtLeast(0).On(_Handler).Method("UrlProcessingBegan").WithAnyArguments();

                ExpectProcessedUris(processedUris, null, _Handler, _Directory, engine);
                RunEngine(engine, waitEvent);
                Assert.AreEqual<int>(0, engine.Store.Count);
                CheckErrors(errorCount, webFailureCount);
            }
        }

        [TestMethod]
        /// <summary>Executes the ErrorAndNoReturnTest test.</summary>
        public void ErrorAndNoReturnTest() {
            TestGateway._ThrowFor = ThrowFor.EndRead;
            string[] startingUris = new string[] { "nolinks.html", "test.html" };  //nolinks should go first, and test links back to nolinks
            string[] processedUris = new string[] { "test.html" };
            string[] historyFiles = new string[] { };
            int workerCount = 1;
            int errorCount = 0;
            int webFailureCount = 1;

            PerformTest(startingUris, processedUris, historyFiles, workerCount, errorCount, webFailureCount, null);
        }

        [TestMethod]
        /// <summary>Executes the TestSimpleError test.</summary>
        public void TestSimpleError() {
            string[] initial = new string[] { "test.html", "atest.html" };
            string[] final = new string[] { "test.html", "nolinks.html" };
            PerformTest(initial, final, null, 1, 0, 1, null);
        }




        void PerformTest(string[] startingUris, string[] processedUris, string[] historyFiles, int workerCount, int errorCount, int webFailureCount, NMock2.IAction processedAction) {
            LoadHistoryFiles(historyFiles, _Directory);
            string[] initialUriList = CreateUriList(startingUris, _Directory);
            using (Eas.WebCrawler.Interfaces.WebCrawlerEngine engine = TestHelper.CreateEngine(new TestGateway(), _Handler, _Directory, workerCount, -1, initialUriList)) {
                Eas.WebCrawler.Common.Threading.EventHandlerCompletionEvent waitEvent = PrepareEngine(_Handler, engine);
                ExpectProcessedUris(processedUris, processedAction, _Handler, _Directory, engine);
                RunEngine(engine, waitEvent);
                CheckErrors(errorCount, webFailureCount);
            }
        }

        private void CheckErrors(int errorCount, int webFailureCount) {
            Assert.AreEqual<int>(errorCount, LogErrorAction.errors.Count);
            Assert.AreEqual<int>(webFailureCount, LogWebFailureAction.failures.Count);
        }

        private void RunEngine(Eas.WebCrawler.Interfaces.WebCrawlerEngine engine, Eas.WebCrawler.Common.Threading.EventHandlerCompletionEvent waitEvent) {
            try {
                engine.Start();
                waitEvent.WaitOne();
            }
            catch (System.Exception ex) {
                Console.WriteLine(ex.ToString());
            }
        }

        private void ExpectProcessedUris(string[] processedUris, NMock2.IAction processedAction, TestEventHandler handler, Eas.WebCrawler.Common.Test.TestDirectory directory, Eas.WebCrawler.Interfaces.WebCrawlerEngine engine) {
            UriProcessedCheckAction action = new UriProcessedCheckAction(_ExpectedUris);
            foreach (string uri in CreateUriList(processedUris, _Directory)) {
                _ExpectedUris.Add(new Uri(uri), null);
                if (processedAction == null) {
                    NMock2.Expect.Once.On(_Handler).Method("UrlProcessed").WithAnyArguments().Will(action);
                }
                else {
                    NMock2.Expect.Once.On(_Handler).Method("UrlProcessed").WithAnyArguments().Will(action, processedAction);
                }
            }
            Assert.AreEqual<int>(processedUris.Length, _ExpectedUris.Count);
        }

        private Eas.WebCrawler.Common.Threading.EventHandlerCompletionEvent PrepareEngine(TestEventHandler handler, Eas.WebCrawler.Interfaces.WebCrawlerEngine engine) {
            Eas.WebCrawler.Common.Threading.EventHandlerCompletionEvent waitEvent = new Eas.WebCrawler.Common.Threading.EventHandlerCompletionEvent();
            engine.Events.Stopped += waitEvent.CreateEventHandler();
            engine.Events.TraceLevel = Eas.WebCrawler.Interfaces.TraceLevel.Detailed;
            LogErrorAction.errors.Clear();
            NMock2.Expect.AtLeast(0).On(handler).Method("Error").WithAnyArguments().Will(new LogErrorAction());
            NMock2.Expect.AtLeast(0).On(handler).Method("Trace").WithAnyArguments().Will(new LogTraceAction());
            NMock2.Expect.AtLeast(0).On(handler).Method("UrlFailed").WithAnyArguments().Will(new LogWebFailureAction());
            NMock2.Expect.Once.On(handler).Method("BeforeStart").WithAnyArguments();
            NMock2.Expect.Once.On(handler).Method("AfterStop").WithAnyArguments();
            NMock2.Expect.AtLeast(0).On(handler).Method("UrlProcessingBegan").WithAnyArguments();


            return waitEvent;
        }

        private void LoadHistoryFiles(string[] historyFiles, Eas.WebCrawler.Common.Test.TestDirectory directory) {
            if (historyFiles != null) {
                foreach (string file in historyFiles) {
                    TestHelper.AddToHistoryFile(directory, file);
                }
            }
        }

        private string[] CreateUriList(string[] startingUris, Eas.WebCrawler.Common.Test.TestDirectory directory) {
            string[] initialUriList = new string[startingUris.Length];

            for (int i = 0; i < startingUris.Length; i++) {
                initialUriList[i] = "file://" + directory.CreateFileName(startingUris[i]);
            }
            return initialUriList;
        }
    }
}
